/**
 * use TypeScript
 * 
 * 微信小程序加密传输方法
 * 算法：AES-128-CBC 同步微信服务器下发的加密方式，为了后端的加解密统一与方便
 * 填充：Pkcs7
 *
 * App.js 封装时依赖包
 * js-base64
 *
 * 为保证加密中密文在一定程度上能够保持安全，且传输中保持 iv 和 key 也不泄露的情况下，其中
 *
 * iv： 采用什么作为 iv 与后端约定，不多哔哔
 * key：采用什么作为 key 与后端约定，不多哔哔
 *
 * ##特别注意：为什么 iv key 必须要截取16个长度字符【不能多也不能少】，别问太多，因为下面引用的 ase.js 这么工作的 是否要深入研究.....呵呵呵
 * 也有可能是与后端通信解密的问题造成必须16个长度字符
 *
 * ### 与后端通信语言 PHP
 * ### 但是PHP自身使用 openssl_encrypt[decrypt] 貌似 key 也不需要非得 16 个长度，这特么.....
 */

//  引入后 封装到 App.js 提供全局的方法
//  开发时使用的加解密方法
/**
 * 提供一个 【未登录】时AES解密KEY，适用于未登录解密使用，至于 IV 和后端沟通
 * globalData.aesKey_Dynamic: "BPFMDTNLGQX....要足够16个字符（可 字母数字混合，最好不要出现各种符号(like: &*.!#)请不要问，不知道，懒得知道）"【后端也需要同步留存，否则【未登录】无法加解密】
 *
 * 这里加密方式 采用 仅【未登录】状态下需要传输 真实 iv 以外，获得登录态的情况下 传输的 iv [key] 都是假的，真的在哪里怎么用，不多哔哔以免泄露加密使用的内容
 *
 * 以下方法，获得登录态以后的加密都相对安全，因为 iv 和 key 与后端怎么约定的只有开发员知道 且【获得登录态】时 其实是无需发送iv 和 key 后端知道怎么去解密
 * ^ 所以【登录态】下发送的加密内容中的 iv [key] 都是虚假的
 *
 * 为什么要用 js-base64 和 onRandomString 辅助的原因
 *
 * 欢迎喷这个加密过程，世界上没有无Bug的东西，这里即使泄密了，修补后可修复加密的内容再次保持相对的安全可言
 *
 * 小程序内随相对安全，但不是绝对的，加密的目的是为了增加破解成本，各自安好
 *
 * 不足的欢迎 喷 和 fix
 *
 * TypeScript 需要转换的自行解决
 *
    //  加密（已登录）
    onEncrypt(rawData: any) {
        let timestamps: any = new Date()
        rawData['timestamps'] = Date.parse(timestamps) / 1000;  // 增加时间戳

        rawData = JSON.stringify(rawData);
        return {
            raw: isEncrypt(rawData, this.onAesInfo().iv, this.onAesInfo().key),
            iv: Base64.encode(this.onRandomString(16))  //  【获得登录态】假的 iv 被劫持了也就这样
        }
    },

    //  加密（未登录）
    onNoLoginEncrypt(rawData: any) {
        let timestamps: any = new Date()
        let iv = this.onRandomString(16);
        rawData['timestamps'] = Date.parse(timestamps) / 1000;  // 增加时间戳

        rawData = JSON.stringify(rawData);
        return {
            raw: isEncrypt(rawData, iv, this.globalData.aesKey_Dynamic),
            iv: Base64.encode(iv)   //  注意 这个 iv 是真的【未登录】时可能都不安全，万一泄密了加密使用的各种
        }
    },

    //  解密（已登录）
    onDecrypt(rawData: any) {
        //  【获得登录态】这个 iv 也是假的
        return isDecrypt(rawData, this.onAesInfo().iv, this.onAesInfo().key);
    },

    //  未登录的解密方法
    onNoLoginDecrypt(rawData: any, iv: string) {
        //  注意 这里所接收到的 iv 也是真的
        return isDecrypt(rawData, Base64.decode(iv), this.globalData.aesKey_Dynamic);
    },

    // 统一构造 AES加密信息 Key IV 【获得登录态】时
    onAesInfo() {
        let aes: object;
        let getKey, getIv;
        getKey = 与后端沟通后 ===> 自由构建的16个长度的字符作为 KEY;
        getIv  = 与后端沟通后 ===> 自由构建的16个长度的字符作为 IV;
        aes = {
            key: getKey,
            iv: getIv
        }
        return aes;
    },

    // 随机数（&或伪造 IV）
    // @param len   返回的长度，默认：6
    onRandomString(len: number = 6) {
        len = len || 32;
        let $chars = 'ABCDEFHGIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';  // 全量字符 无视干扰
        let maxPos = $chars.length;
        let randomStr: string = '';
        for (let i = 0; i < len; i++) {
            randomStr += $chars.charAt(Math.floor(Math.random() * maxPos));
        }
        return randomStr;    // 输出
    },
*/

const aes = require("./aes.js")
/**
 * 加密数据（发送）
 * @param raw   发送到API接口的表单数据
 * @param iv    iv向量
 * @param key   加密key
 */
export function isEncrypt(raw: any, iv: string, key: string) {
    // iv: 与后端约定  key: 与后端约定
    // 务必 保持 iv 和 key 相对动态可言，保证传输数据中 如果被篡改、劫持、泄密时有机会可防止
    let option: object; // 加密设置 保持此方式，否则后端无法解密
    option = {
        iv: aes.CryptoJS.enc.Utf8.parse(iv),
        mode: aes.CryptoJS.mode.CBC,
        padding: aes.CryptoJS.pad.Pkcs7
    }
    key = aes.CryptoJS.enc.Utf8.parse(key)

    let rawData = aes.CryptoJS.enc.Utf8.parse(raw);
    let encodeStr = aes.CryptoJS.AES.encrypt(rawData, key, option);
    //返回base64加密结果
    return encodeStr.toString();
}

/**
 * 解密数据（接收）
 * @param data  代解密数据
 * @param iv    iv向量
 * @param key   加密key
 */
export function isDecrypt(data: any, iv: any, key: any) {
    let option: object;
    option = {
        iv: aes.CryptoJS.enc.Utf8.parse(iv),
        mode: aes.CryptoJS.mode.CBC,
        padding: aes.CryptoJS.pad.Pkcs7
    }
    // data是base64编码数据
    let decrypt = aes.CryptoJS.AES.decrypt(data, aes.CryptoJS.enc.Utf8.parse(key), option);
    let decryptedStr = decrypt.toString(aes.CryptoJS.enc.Utf8);
    return decryptedStr;
}